/***************************************************************
 * Name:      mCanvas.h
 * Purpose:   opengl
 * Author:    Riku Maikola (rpm@ac-mapa.org)
 * Created:   2012-12-21
 * Copyright: Riku Maikola (rpm.ac-mapa.org)
 * License:
 **************************************************************/

#include "mCanvas.h"

//***************//
void mCanvas::Init()
{
    glewInit();
    Shader( 0 , "shader/proj.vertex"        , "shader/color.frag" );           //**** 0 ****//
    Shader( 1 , "shader/ortho.vertex"       , "shader/color.frag" );           //**** 1 ****//

 //   Shader( 2 , "shader/id_proj.vertex"     , "shader/color.frag" );           //**** 2 ****//
 //   Shader( 3 , "shader/id_ortho.vertex"    , "shader/color.frag" );           //**** 3 ****//

    Shader( 4 , "shader/proj_phong.vertex"  , "shader/phong.frag" );      //**** 4 ****//
    Shader( 5 , "shader/ortho_phong.vertex" , "shader/phong.frag" );     //**** 5 ****//

    Shader( 6 , "shader/texture.vertex" , "shader/texture.frag" );     //**** 5 ****//

    //GENERATE BUFFER NAMES
    glGenBuffers(VBOCOUNT+1, vbos);
    glGenTextures(10,T);
}

/**
void mCanvas::Add(unsigned int id,wxImage img)
{
    int W=img.GetWidth();
	int H=img.GetHeight();

    GLubyte *bitmapData=img.GetData();
    GLubyte *alphaData=img.GetAlpha();

    int bytesPerPixel = img.HasAlpha() ?  4 : 3;
    int imageSize = W * H * bytesPerPixel;
    GLubyte *imageData=new GLubyte[imageSize];

    int rev_val=H-1;

    for(int y=0; y<H; y++)
    {
        for(int x=0; x<W; x++)
        {
            imageData[(x+y*W)*bytesPerPixel+0] = bitmapData[( x+(rev_val-y)*W)*3];
            imageData[(x+y*W)*bytesPerPixel+1] = bitmapData[( x+(rev_val-y)*W)*3 + 1];
            imageData[(x+y*W)*bytesPerPixel+2] = bitmapData[( x+(rev_val-y)*W)*3 + 2];
            if(bytesPerPixel==4)imageData[(x+y*W)*bytesPerPixel+3] = alphaData[ x+(rev_val-y)*W];
        }
    }
    //************************
    glBindTexture(GL_TEXTURE_2D,T[id]);
    glTexImage2D(GL_TEXTURE_2D,0,bytesPerPixel,W,H,0,img.HasAlpha() ? GL_RGBA : GL_RGB,GL_UNSIGNED_BYTE,imageData);

    delete [] imageData;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // GL_LINEAR
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // GL_LINEAR

	//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
};
*/

GLuint loadShader(const std::string filename,GLenum shaderType)
{
    GLuint shaderID = 0;
    std::string str;
    std::ifstream file(filename.c_str() ,  std::ios::in);

    if (file)
    {
        str.assign( ( std::istreambuf_iterator< char >( file ) ), std::istreambuf_iterator< char >() );
        shaderID = glCreateShader( shaderType );
        const GLchar* shaderSource = str.c_str();
        glShaderSource( shaderID, 1, (const GLchar**)&shaderSource, NULL );
        glCompileShader( shaderID );
        GLint shaderCompiled = GL_FALSE;
        glGetShaderiv( shaderID, GL_COMPILE_STATUS, &shaderCompiled );
        if( shaderCompiled != GL_TRUE )
        {
            printf( "Unable to compile shader %d!\n\nSource:\n%s\n", shaderID, shaderSource );
            glDeleteShader( shaderID );
            shaderID = 0;
        }
    }
    else
    {
        printf( "Unable to open file %s\n", filename.c_str() );
    }

    return shaderID;
}

void mCanvas::Shader(uint sh,const std::string vertex_shader,const std::string fragment_shader)
{
    //orthpo shader
    S[sh].program = glCreateProgram();

    glAttachShader( S[sh].program , loadShader( vertex_shader ,GL_VERTEX_SHADER));
    glAttachShader( S[sh].program , loadShader( fragment_shader ,GL_FRAGMENT_SHADER));

    glLinkProgram( S[sh].program );

    S[sh].move = glGetUniformLocation( S[sh].program , "mv_matrix");
    S[sh].view = glGetUniformLocation( S[sh].program , "view_matrix");
    S[sh].proj = glGetUniformLocation( S[sh].program , "proj_matrix");
}
//********************************************//
void mCanvas::Update( GLuint id , DrawBuffer * DB )
{
    if(DB->vbuf.size()==0)return;

    vbo[id].type = DB->Type();
    vbo[id].world = DB->InWorld();
    vbo[id].shader = DB->useShader();
    vbo[id].size = DB->vbuf.size();
    vbo[id].show = true;

    glBindBuffer(GL_ARRAY_BUFFER, vbos[id]);
    glBufferData(GL_ARRAY_BUFFER, vbo[id].size * sizeof(vertex), &DB->vbuf.at(0) , GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

};
//*************************************************************************************//
//**                                                                                 **//
//**                                                                                 **//
//**                                                                                 **//
//*************************************************************************************//
#define BUFFER_OFFSET(i) ((void*)(i))
//**************************************************************//
void mCanvas::window(uint id , uint Sh,double width,double height)
{
    //if(vbo.size()==0)return;
    if(vbos[id]<1)return;

    double sizeW=150,sizeH=150,scale;

    if( (width*2)>height)scale = sizeW/(width*2);
    else scale = sizeH/height;

    glClearDepth(1.0);
    glClear(GL_DEPTH_BUFFER_BIT);

    glViewport((GLint)W-sizeW,(GLint)H-sizeH,sizeW,sizeH);

    //proj_matrix = vmath::ortho(  -sizeW/2 , sizeH/2 , -sizeW/2 , sizeH/2 ,  0.1f  ,  1000.0f  );

    vmath::mat4 pos = vmath::translate( 0.0f , (float)-(sizeH/2) , 0.0f) * vmath::rotate( 90.0f , -1.0f , 0.0f , 0.0f);;
    pos *= vmath::scale(( float)scale );

    glBindBuffer(GL_ARRAY_BUFFER, vbos[id]);

    glUseProgram(  S[Sh].program );

    glVertexAttribPointer(0, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , NULL); //vertex
    glVertexAttribPointer(1, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(12) ); //color
    glVertexAttribPointer(2, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(28) ); //normal
    glVertexAttribPointer(3, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(40) ); //ID color

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glEnableVertexAttribArray(3);

    glUniformMatrix4fv( S[Sh].proj, 1, GL_FALSE, proj_matrix);
    glUniformMatrix4fv( S[Sh].view, 1, GL_FALSE, view_matrix);
    glUniformMatrix4fv( S[Sh].move, 1, GL_FALSE, pos);

    glDrawArrays(vbo[id].type, 0, vbo[id].size);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(0);

}
//*************************************************************************************//
//**                                                                                 **//
//**                                                                                 **//
//*************************************************************************************//
void mCanvas::ViewPort()
{
    SetCurrent();
    SetSize(W,H);
    glViewport(0, 0, W, H);

    glEnable(GL_DEPTH_TEST);
    glClearColor(0.8, 0.8, 0.8, 0.0);
    glClearDepth(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

}

//*********************************//
uint mCanvas::PICK(GLuint id,int X,int Y)
{
    ViewPort();
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClearDepth(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLineWidth (5);

    Render(id,mP(0,0),true);

    unsigned char pixel[] = {0,0,0,0};
    unsigned int result=0;

    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);

    glReadPixels(X,Y,1,1,GL_BGR,GL_UNSIGNED_BYTE,pixel);

    glLineWidth (1);

    result = (result << 8) + pixel[3];
    result = (result << 8) + pixel[2];
    result = (result << 8) + pixel[1];
    result = (result << 8) + pixel[0];

    return result;
};

//**
void mCanvas::Render(uint id , mPoint where, bool renderID)
{
    if(vbo[id].show==false)return;
    uint Sh=vbo[id].shader;

    if(renderID)Sh=1;

    glBindBuffer(GL_ARRAY_BUFFER, vbos[id]);
    vmath::mat4 pos = mv_matrix*vmath::translate( (float)where.x , (float)where.y , (float)where.z);
    glUseProgram( S[Sh].program );

    glVertexAttribPointer(0, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , NULL);                           //vertex
    if(renderID)
        glVertexAttribPointer(1, 4, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(40) ); //ID color
    else
        glVertexAttribPointer(1, 4, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(12) );         //color
    glVertexAttribPointer(2, 3, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(28) );             //normal
    glVertexAttribPointer(3, 2, GL_FLOAT , GL_FALSE , sizeof(vertex) , BUFFER_OFFSET(56) );             //ID texture

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glEnableVertexAttribArray(3);

    glUniformMatrix4fv( S[Sh].proj, 1, GL_FALSE, proj_matrix);
    glUniformMatrix4fv( S[Sh].view, 1, GL_FALSE, view_matrix);
    glUniformMatrix4fv( S[Sh].move, 1, GL_FALSE, pos);

    glDrawArrays(vbo[id].type, 0, vbo[id].size);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glUseProgram(0);
};
